home *** CD-ROM | disk | FTP | other *** search
- Version 0.43 1/4/94
-
- I. Here is the code for and description of "whatsnewd" and "findwhatsnew":
-
- (Connect your Gopher to "gopher.eff.org", port "5070" to play with it.)
-
- What it does:
-
- When a user gives a date (e.g. "1 day ago") or enters a dated bookmark
- (generated by a previous query), he or she gets a gopher menu of the
- gopher items that are new or changed since that date.
-
- The generated bookmarks allow a user to see just those items that
- have been created or changed since his or her last query.
-
- I won't have time to work any more on this for a while. But here is
- the code, "as is". It consists of two programs:
- findwhatsnew - which updates a database of gopher items
- whatsnewd - a gopher protocol server
- Both are written in Perl.
-
- You're are encouraged to add to or modify this code.
- (And to improve this documentation.)
-
- [Joaquim Baptista, px@fct.unl.pt, has done this creating "traveller" a
- hybrid of "whatsnew" and "veronica" with other new features. To try
- it, do "gopher -ptravel gopher.fct.unl.pt 4320". As of today,
- the software is in boombox.micro.umn.edu:pub/gopher/incoming.]
-
- -- Carl Kadie (kadie@eff.org) 09/21/93
-
- II. ============== About findwhatsnew ==============
-
- USAGE findwhatsnew NAME PATH SERVER PORT DEPTH
- OR findwhatsnew NAME
-
- For example:
- findwhatsnew caf "1/academic" gopher.eff.org 70 3
- findwhatsnew caf
-
- NAME can include a directory path. For example:
-
- findwhatsnew /net/kragar/serv/gopher/whatsnewd/data/caf "1/academic" gopher.eff.org 70 3
-
- Notes:
-
- 1. You will eventually need to create a whatsnewd directory.
- Ours is a subdirectory of our main "gopher" directory.
- It should have three subdirectories:
- bin data logs
- "data" is where "whatsnewd" expects to see the database
- that "findwhatsnew" creates.
-
- 2. Run "findwhatsnew" first with a name and all the parameters
-
- findwhatsnew NAME PATH SERVER PORT DEPTH
-
- After that, you need not give the parameters (unless you want to
- change them). e.g.
-
- findwhatsnew NAME
-
- will usually be enough.
-
- 3. The parameters
-
- NAME -- will become the short name used to make files.
- PATH -- a gopher path
- SERVER -- aka HOST
- PORT -- usually 70
- DEPTH -- start with 1 or 2 and slowly increase until
- you are sure you have your kill and keep files the way you want
-
- 4. Files (only NAME.kill is user created)
-
- NAME.times - text file. Each line is a time stamp and
- bookmark. Here is an example line:
-
- 19921208075043<tab>0cafv02n56<tab>0/academic/news/cafv02n56<tab>gopher.eff.org<tab>70
-
- Dates are of the form YYYYMMDDHHMMSS and are GMT.
-
- NAME.bak - the old version of NAME.times
- NAME.tmp - temporary version of NAME.times
- (NAME.times is replaced by NAME.tmp at the very end of processing.)
- NAME.kill - kills and keeps certain paths. Examples are enclosed.
- NAME.save - the parameters for the program.
- NAME.tree - the explored gopher tree (nice and indented)
- This is very handy for tuning the NAME.kill file.
-
- 5. Kill file:
-
- A kill file is used to stop whatsnewd from searching places you don't
- what it too look.
-
- Comment lines in a kill file start with a "#". Blanks lines are OK,
- too.
-
- The four commands are:
-
- kill PATTERN
- keep PATTERN
- kill-subs-of PATTERN
- keep PATTERN
-
- Where PATTERN is a Perl regular expression. A kill file
- may contain many instances of each type of command.
-
- Every time an item is found, these patterns are consulted. If the item
- matches at least one "keep" pattern and doesn't match any "kill"
- patterns, it is included in the database. If an item matches at least
- one "keeps-subs-of" pattern and doesn't machine any
- "kill-subs-of" patterns, its subitems (if any) will be explored.
-
- For the purpose of this pattern matching an item is described as
- a string of the form:
-
- "
- Name0=NAME0VALUE
- Port0=PORT0VALUE
- Path0=PATH0VALUE
- Host0=HOST0VALUE
- Name=NAMEVALUE
- Type=TYPEVALUE
- Port=PORTVALUE
- Path=PATHVALUE
- Host=HOSTVALUE
- "
-
- Where all the fields ending in 0 have values for
- the item's parent. And the non-0-ending fields have values
- for the item.
-
- 6. Example kill files:
-
- ============== caf.kill ==============
- # Stops it from exploring "whatsnew" stuff
- kill-subs-of \nPort0=5070\n
- # Stops it from exploring articles of the Campus Newspapers
- kill-subs-of \nPath=1/academic/newspapers
- # Don't look at subdirectories of items whose path starts "m/"
- kill-subs-of \nPath=m/
- # Don't follow gopher link for library material back to top of CAF
- kill \nPath=1/academic/library/academic\n
- # Keep only those items reached from gopher.eff.org
- keep \nHost0=gopher.eff.org\n
- keep \nHost0=kragar.eff.org\n
-
- ============== cso.kill ===================
- # Don't go more than one step about from a uiuc.edu machine.
- keep \nHost0=.*\.uiuc\.edu\n
-
- # Don't go from a department machine back to the main campus gopher machine
- kill \nType=1\nPort=70\nPath=(1/)?\nHost=harpoon\.cso\.uiuc\.edu\n
- kill \nType=1\nPort=70\nPath=(1/)?\nHost=gopher\.(cso\.)?uiuc\.edu\n
- kill \nHost0=s\.psych\.uiuc\.edu\n(.|\n)*\nHost=harpoon\.cso.\uiuc\.edu\n
- kill \nHost0=cyberdyne\.ece\.uiuc\.edu\n(.|\n)*\nHost=gopher.\uiuc\.edu\n
- kill \nHost0=s\.psych\.uiuc\.edu\n(.|\n)*\nHost=gopher\.uiuc\.edu\n
-
- # Kill an experimental branch
- kill-subs-of \nPath=1/Information about Gopher/exp\n
-
- # Stops it from exploring "whatsnew" bookmarks
- kill \nPath=.* gophergmt\n
-
- # Don't look at subdirectories of items whose path starts "m/"
- kill-subs-of \nPath=m/
-
- kill-subs-of \nPath=1/FTP\n
- kill-subs-of \nPath=1/Phone Books\n
- kill-subs-of \nPath=1/UI/Timetables\n
-
- # Don't explore the Placement Office
- kill \nPort=15000\n(.|\n)*\nHost=harpoon\.cso\.uiuc\.edu\n
- # Don't explore the newspaper
- kill \nPath=1/UI/DI\n
- kill-subs-of \nPath=1/News/Daily Illini Newspaper\n
- # Don't explore Student Employment
- kill-subs-of \nPath=1/UI/FinAid/Student Employment\n
-
- kill-subs-of \nPath=1/Manuals/Unix\n
- kill \nPath=1/Manuals/SM/
- kill-subs-of \nPath=1/Forecasts\n
- kill-subs-of \nPath=1/Current Conditions\n
- kill \nPath=ftp:
- kill-subs-of \nPort=70\nPath=1/Forecast\nHost=wx\.atmos\.uiuc\.edu\n
- kill-subs-of \nPort=70\nPath=1/Illinois\nHost=wx\.atmos\.uiuc\.edu\n
- kill-subs-of \nPort=70\nPath=1/Images\nHost=wx\.atmos\.uiuc\.edu\n
- kill-subs-of \nPort=70\nPath=1/Surface\nHost=wx\.atmos\.uiuc\.edu\n
- kill-subs-of \nPort=70\nPath=1/Upper\nHost=wx\.atmos\.uiuc\.edu\n
- kill-subs-of \nPort=70\nPath=1/Severe\nHost=wx\.atmos\.uiuc\.edu\n
- kill-subs-of \nPort=70\nPath=1/Servers\nHost=wx\.atmos\.uiuc\.edu\n
-
- # Don't look at individual articles in _Inside Illinois_
- # Kill both ways to get to the articles
- kill-subs-of \nPath0=1/UI/II\n
- kill-subs-of \nPath0=1/News/Inside Illinois, the Faculty-Staff Newspaper\n
-
- # Don't look at individual books
- kill-subs-of \nPath0=1/library\n
-
- #Kill specific manuals and scripts
- kill-subs-of \nPath0=1/Documents\nHost0=wx\.atmos\.uiuc\.edu\n
-
-
- =======================================================
-
- 7. What exactly the program does
-
- It reads the old database of time stamps and bookmarks. If it sees
- bookmarks, identical except from the time stamp, it uses the older
- time stamp.
-
- It explores the gopher tree within the bounderies set by the depth
- bound and the kill file. It remembers the Name/Port/Path/Host of the
- items that it sees. Duplicates are not explored.
-
- If it finds an item that is mentioned in the old database,
- it puts the item in the new database with the old time stamp.
-
- If it finds an item that is not mentioned in the old database,
- it puts the item in the new database, stamped with the current time.
-
- Items from the old database that are not found, are put in the new
- database anyway. They are marked "Couldn't connect: ". From time to
- time, you may want to delete these items manually from the database
- (*.times file).
-
- III. ============== About whatsnewd ==============
-
- USAGE: whatsnewd -lLOGFILE Datadir
-
- "whatsnewd" is a gopher-protocol server.
-
- If a user gives a date (e.g. "1 day ago") or enters a dated bookmark
- (generated by a previous query), he or she gets a gopher menu of the
- gopher items that are new or changed since that date.
-
- Its input is:
- DATADIR - a directory containing *.times and *.save files
- as created by "findwhatsnew"
- LOGFILE - a log file
- stdin - a command (i.e. gopher path). For example:
- 1\ : create a menu for every database you know about
- 1\NAME : create a menu for the NAME.times database
- 0\NAME : create information about the NAME.times database
- 7\NAME<tab>STRING : Answer a query about NAME
- 1\NAME\STRING : same as 7\NAME<tab>STRING
-
- Its output is:
- stdout - gopher menus or text items
-
- 2. You can play with the program before installing it as a daemon.
- Just run it (don't forget parameters) and type into standard input.
-
- 3. Ultimately it should be installed as a network daemon.
- (My sysadmin, Chris Davis, ckd@eff.org, did this for me).
-
- Port 5070 is suggested, but any port should work. Here is the line
- from gopher.eff.org's "etc/inetd":
-
- gwn stream tcp nowait gopher /serv/gopher/whatsnewd/bin/whatsnewd whatsnewd -l/serv/gopher/whatsnewd/logs/whatsnewd.log /serv/gopher/whatsnewd/data
-
- And here is the line from gopher.eff.org's "services":
-
- services:gwn 5070/tcp whatsnewd # gopher what's new
-
- Here is where we've but it's files:
- The program is:
- /serv/gopher/whatsnewd/bin/whatsnewd
- The log file is:
- /serv/gopher/whatsnewd/logs/whatsnewd.log
- And the *.times and *.save files live in:
- /serv/gopher/whatsnewd/data
-
- IV. The Code: ========== findwhatsnew =================
- #!/usr/bin/perl
- # !/local/all/perl
-
-
- # History
- # July 24, 1993 - mark old saved items to make them easier to dump
- # if two identical items are in the *.times file,
- # use the older date
- # July 7, 1993 - changed a bit to make work with new minor release of Perl
- # Jan 1, 1992 - save old items, even if not found
- # Dec 9, 1992 - output *.tree file
- # Dec 3, 1992 - change to findwhatsnew
- # Nov 23, 1992 cmk - initial development of gopherdiff
-
- # Bugs: if gopher can supply time, that should be used instead.
-
- $sort = "/bin/sort";
- $rm = "/usr/bin/rm";
-
- $| = 1;
-
- ($cursec,$curmin,$curhour,$curmday,$curmon,$curyear,
- $curwday,$curyday,$curisdst)
- = gmtime(time);
- $curmon++;
- if ($curyear > 90) {$curyear = $curyear + 1900;}
- elsif ($curyear < 20) {$curyear = $curyear + 2000;}
- else {die "Sorry, limited to years between 1990 and 2020";}
-
- $curdate = sprintf("%.4d%.2d%.2d%.2d%.2d%.2d",$curyear,$curmon,$curmday,$curhour,$curmin,$cursec);
-
- $cannotconnect = "Couldn't connect: ";
- ($gopherdir,$name) = @ARGV[0] =~ /(.*)([^\/]*)/;
- #print STDERR "< $gopherdir $name >\n";
-
- $bakfile = "$gopherdir$name.bak";
- $oldfile = "$gopherdir$name.times";
- if (open(OLD,"<$oldfile")) {
- while (<OLD>) {
- ($date2,$tname2,$rest2) = /^([^\t]*)\t([^\t]*)\t(.*).$/;
- #print STDERR
- # "date2=$date2\n\ttname2=$tname2\n\trest2=$rest2\n";
- $rest2 =~ s/^($cannotconnect)+//;
- $tname2 =~ s/^($cannotconnect)+//;
- #print STDERR
- # "date2=$date2\n\ttname2=$tname2\n\trest2=$rest2\n";
- $rem2 = @REMEM{$rest2};
- #print "before:$rest2->@REMEM{$rest2}\n";
- # if new "tag" then just remember
- if ($rem2 eq "") {
- @REMEM{$rest2} = "$date2\t$tname2";
- } else { # if have seen "tag" before keep older tag
- ($date22,$tname22) = split("\t",$rem2);
- #print "$date2<$date22\n";
- if ($date2<$date22) {
- @REMEM{$rest2} = "$date2\t$tname2";
- }
- }
- #print "after:$rest2->@REMEM{$rest2}\n";
- }
- close(OLD);
- }
- #print STDERR "OLD is loaded\n";
-
- $newfile = "$gopherdir$name.tmp";
- open(NEW,">$newfile")||die("Cannot open $newfile");
- $oldhandle = select(NEW); $| = 1; select ($oldhandle);
-
- $treefile = "$gopherdir$name.tree";
- open(TREE,">$treefile")||die("Cannot open $treefile");
- $oldhandle = select(TREE); $| = 1; select ($oldhandle);
-
- @kills = &LOAD_KILL_FILE($gopherdir,$name);
-
- $save = "$gopherdir$name.save";
- if ($#ARGV == 4) {
- open (SAVE,">$save");
- print SAVE join("\n",@ARGV);
- print SAVE "\n";
- close(SAVE);
- } elsif ($#ARGV == 0) {
- @ARGV=();
- open (SAVE,"<$save");
- while(<SAVE>){chop; push(@ARGV,$_);}
- close(SAVE);
- } else {
- print "USAGE findwhatsnew NAME PATH SERVER PORT DEPTH\n";
- print "OR findwhatsnew NAME\n";
- exit;
- }
- $oridepth = @ARGV[4];
- shift;
- $start_gopher = "gopher -p \"@ARGV[0]\" @ARGV[1] @ARGV[2]";
- push(@ARGV,"");
- &RECUR(@ARGV);
- # output old items, not seen this time
- foreach $key9 (keys(%REMEM)) {
- $rem9 = @REMEM{$key9};
- if ($rem9 ne "1") {
- $rem9 =~ s/\t/\t$cannotconnect/;
- print NEW "$rem9\t$key9\r\n";
- }
- }
- unlink($bakfile) if -e $bakfile;
- rename($oldfile,$bakfile);
- close;
- exec("$sort < $newfile > $oldfile;$rm $newfile");
-
- sub RECUR {
- local($path,$server,$port,$depth) = @_;
- local($newtype,$newline,$name1,$path1,$server1,$port1);
- local($dir,$rem,$c);
-
- #print STDERR "<< $server,$port,$path,$depth >>\n";
-
- $dir = &CLIENT($server,$port,"$path\r\n");
-
- #print STDERR "<2> $dir\n";
- foreach $_ (split("\n",$dir)) {
- #print STDERR $_;
- $item = $_;
- ($type1,$name1,$rest1,$path1,$server1,$port1)
- = /^(.)([^\t]*)\t(([^\t]*)\t([^\t]*)\t([^\t]*).*)$/;
- $tname1= "$type1$name1";
- #print STDERR ">3>$type1,$name1,$rest1,$path1,$server1,$port1\n";
- #print $rest1;
- $rem = @REMEM{$rest1};
- ($remdate,$remname) = split("\t",$rem);
- #print "<rem< $rem, $remdate, $remname >>\n";
- $killlong ="\nName0=$name\nPort0=$port1\nPath0=$path\nHost0=$server\nName=$name1\nType=$type1\nPort=$port1\nPath=$path1\nHost=$server1\n";
- study($killong);
- $*=1;
- $k1 = !(@kills{'kill'} && ($killlong =~ /@kills{'kill'}/));
- $k2 = (!@kills{'keep'} || ($killlong =~ /@kills{'keep'}/));
- #print STDERR ">4>Killlong=$killlong\n";
- #print STDERR ">5>don't kill=$k1. keep=$k2 rem=$rem\n";
- if ($k1 && $k2 && $rem != 1) {
- $*=0;
- #print STDERR ">6>\n";
- if ($remname ne $tname1){
- print NEW "$curdate\t$item\r\n";
- } else {
- print NEW "$remdate\t$item\r\n";
- }
- #print STDERR ">7>\n";
- $indent = ($oridepth - $depth) * 3 + 1;
- printf(TREE "%${indent}d:%s\n",$depth,$item);
- @REMEM{$rest1} = 1;
- #print STDERR ">8>\n";
- if ($type1 ==1) {
- $k1s = !(@kills{'kill-subs-of'} &&
- ($killlong =~ /@kills{'kill-subs-of'}/));
- $k2s = (!@kills{'keep'-subs-of} ||
- ($killlong =~ /@kills{'keep-sub-of'}/));
- #print "don't kill sub=$k1s. keep sub=$k2s\n";
- #print STDERR ">9>\n";
- if ($k1s && $k2s) {
- if ($depth >0) {
- do &RECUR($path1,$server1,$port1,$depth-1);
- } else {
- #print STDERR "Depth bound reached. Will not explore:\n";
- #print STDERR "$name1\n $path1 $server1 $port1\n\n";
- }}
- #print STDERR ">10>\n";
- }}
- #print STDERR ">11>\n";
- $*=0;
- }
- 1;
- }
-
-
- # Based on "client" on page 344 of _Programming Perl_ by Wall and Schwartz
-
- sub CLIENT {
-
- local($them,$port,$input) = @_;
- local($client);
-
- if ($them eq "error.host") { return("");}
-
- $port = 2345 unless $port;
- $them = 'localhost' unless $them;
-
- $AF_INET =2;
- $SOCK_STREAM = 1;
-
- $SIG{'INT'} = 'dokill';
-
- $sockaddr = 'S n a4 x8';
-
- chop($hostname = `hostname`);
-
- ($name,$aliases,$proto) = getprotobyname('tcp');
- ($name,$aliases,$port) = getservbyname($port,'tcp')
- unless $port =~ /^\d+$/;;
- ($name,$aliases,$type,$len,$thisaddr) = gethostbyname($hostname);
- ($name,$aliases,$type,$len,$thataddr) = gethostbyname($them);
-
- $this = pack($sockaddr,$AF_INET, 0, $thisaddr);
- $that = pack($sockaddr,$AF_INET, $port, $thataddr);
-
- # Make the socket filehandle
- socket(S, $AF_INET, $SOCK_STREAM, $proto)|| die $!;
-
-
- # Give the socket an address.
- bind(S, $this) || die $!;
-
- # Call up the server.
- if (connect(S,$that)) {
- #print "connect ok\n";
- }
- else {
- #print STDERR "CANNOT CONNECT TO $them $port $input $!\n";
- return("");
- }
-
- # Set socket to be command buffered.
-
- $oldhandle = select(S); $| = 1; select ($oldhandle);
-
- #print STDERR $input;
- print S $input;
-
- READ: while (<S>) {
- #print STDERR;
- chop;chop;
- last READ if ($_ eq ".");
- $client .= "$_\n";
- }
- #print STDERR "done with client\n";
- return($client);
- }
-
- sub LOAD_KILL_FILE {
- local($gopherdir,$name) = @_;
- local($killfile) = "$gopherdir$name.kill";
- local(@kills) = ();
- @kills{'kill'} = '0'; @kills{'kill-subs-of'} = '0';
- @kills{'keep'} = '0'; @kills{'keep-subs-of'} = '0';
- if (open(KILL,"<$killfile")) {
- while (<KILL>) {
- if (!/^\s*(#.*)?$/) {
- /(\S+) (.*)/;
- chop;
- local($so_far) = @kills{$1};
- if ($so_far eq '') {die "Kill file command $1 not recognized";}
- if ($so_far eq '0')
- {@kills{$1} = '';} else {@kills{$1} .= '|';}
- @kills{$1} .= $2;
- #print "kills{$1} = @kills{$1}\n";
- }}
- }
- return(@kills);
- }
-
- V. More code: ======= whatsnewd ==========
- #!/usr/bin/perl
-
- # History
- # Jan 4, 1993 -- allow "+" to be used in place of " " (for www compatiblity)
- # July 29, 1993 - adding code to allow it to work with sequence numbers
- # (but sequence numbers aren't used)
- # Dec 8, 1992 - fixed bug cause by limit and date interaction
- # Dec 3, 1992 - Carl M. Kadie - initial development
-
- # Bugs and Limitations:
- # Does a linear, rather than binary search
- # Only works with GMT and relative times, no local times
- # The termcap gopher client will highlight words from search string
-
- # Assumptions:
- # Assumes item list is sorted.
- # (this is used to find date for new bookmark)
-
- # Possibilities:
- # Could findwhatsnew could remember menu selection paths
- # Could use sequence numbers rather than dates.
-
- require "getopts.pl";
- &Getopts("l:");
-
- if ($opt_l) {
- $logfile = $opt_l;
- } else {
- $logfile = "/dev/null";
- }
-
-
- if ($#ARGV == 0) {
- ($whatsnewdir) = @ARGV;
- } else {
- print "USAGE: whatsnewd -lLOGFILE Datadir\n";
- exit;
- }
-
- $host = `hostname`;
- $default_limit = 100;
- $oldext = "times";
- $saveext = "save";
- chop($host);
-
- @seconds{'s'} = 1;
- @seconds{'m'} = @seconds{'s'} * 60;
- @seconds{'h'} = @seconds{'m'} * 60;
- @seconds{'d'} = @seconds{'h'} * 24;
- @seconds{'w'} = @seconds{'d'} * 7;
-
- &SERVER($port);
- #&TESTSERVER($port);
-
- sub TESTSERVER {
- $input = <STDIN>;
- chop($input);
- open(NS,">del");
- &WHATSNEW($input);
- }
-
- sub SERVER {
-
- $sockaddr = 'S n a4 x8';
- $mysockaddr = getsockname(STDIN);
- $remsockaddr = getpeername(STDIN);
- if ($mysockaddr) {
- ($family,$myport,$myaddr)
- = unpack($sockaddr,$mysockaddr);
- ($remfamily,$remport,$remaddr)
- = unpack($sockaddr,$remsockaddr);
- ($theirname) = gethostbyaddr($remaddr,2);
- } else {
- $myport = 5070;
- $theirname = "justtesting.debug";
- }
- $input = <STDIN>;
- chop($input);
- $input =~ s/\r$//; # remove trailing \r if any
- $input =~ s/\+/ /g; # change +'s to blanks
- $input =~ s/\?/\t/g; # change ?'s to tabs
- #print $input;
- &WHATSNEW($input);
- }
-
- sub WHATSNEW {
-
- ($path,$search) = split("\t",@_[0]);
- ($type,$short_name,$style) = split("/",$path);
- if ($type eq '') {$type = 1;}
- if (($type ==1) && ($short_name eq '' || $short_name eq '.')) {
- opendir(DIR,$whatsnewdir) || die ($whatsnewdir,": ",$!);
- @timefiles = grep(/\.$oldext$/,readdir(DIR));
- foreach $timefile (@timefiles) {
- $timefile =~ /(.*)\.$oldext$/;
- local($short_name) = $1;
- local($path1,$host1,$port1) = &GET_GOPHER_REF($1);
- local($date) = &LASTDATE($short_name);
- &PRINT_EXTRA($short_name,$path1,$host1,$port1,$date,0,"about,whatsnew,top");
- }
- closedir(DIR);
- } else {
- local($path1,$host1,$port1) = &GET_GOPHER_REF($short_name);
-
- if ($type == 0 && $style eq '') {
- $curdate = &TIME2GOPHER(time,1);
- open(OLD,"<$whatsnewdir/$short_name.$oldext") || die "$whatsnewdir/$short_name.$oldext: ",$!;
- $_ = <OLD>;
- # what if empty?
- close(OLD);
- ($oldestdate) = /^([^\t]*)\t/;
- $recentdate = &LASTDATE($short_name);
- &LOG_MESS("generated info on $short_name");
- print "About \"What's New in $short_name\":\r
- \r
- To search for items that have been created or changed\r
- since some date, give that date as the search string.\r
- \r
- Dates can be specified in two formats. For example:\r
- 1 day 35 min ago\r
- and\r
- 19921104040013 gophergmt\r
- \r
- In general the formats are:\r
- {W w{weeks}} {D d{ays}} {H h{hours}} {M m{inutes}} {S s{econds}} ago\r
- and\r
- YYYYMMDDHHMMSS gophergmt\r
- \r
- The last three items listed will be:\r
- **About \"What's New in $short_name\"\r
- **The top of the \"$short_name\"'\r
- **Bookmark for future new \"$short_name\" items (...)\r
- \r
- If you save the bookmark now and \"enter\" it in the the future, it will\r
- display the items that have change between now until then.\r
- \r
- ==================Some Status Info ==========================\r
- The current date is $curdate gophergmt. The date of the oldest\r
- $short_name item is $oldestdate gophergmt. The date of the most recent\r
- $short_name item is $recentdate gophergmt.\r
- .\r\n";
- } elsif ($type == 0) {
- print "\r
- The default limit on the number of items returned is $default_limit.\r
- To change this, add, for example, \"30 limit\" or \"no limit\" to your\r
- search string.\r
- .\r\n";
- } elsif ($type == 1 && $style eq "") {
- $date = &LASTDATE($short_name);
- &PRINT_EXTRA($short_name,$path1,$host1,$port1,$date,0,"about,whatsnew,top");
- } elsif ($type == 1 || $type == 7){
- #print "style=$style, search=$search\n";
- if ($type == 1) {$search = $style;}
- #print "style=$style, search=$search\n";
- ($since,$limit) = &PROCESS_DATE($search);
- #print "since=$since, limit=$limit\n";
- $date = $since;
- #$last = &LASTDATE($short_name);
- open(OLD,"<$whatsnewdir/$short_name.$oldext") || die "$whatsnewdir/$short_name.$oldext: ",$!;
- #print "limit=$limit\n";
- local($ll) = $limit;
- &LOG_MESS("searched $short_name with @_[0]");
- LOOP5: while(<OLD>) {
- if (/^[0-9]{14,14}\t/) { # old format
- ($date,$rest) = /^([^\t]*)\t(.*)/;
- } else {
- ($seq,$date,$rest) = /^([^\t]*)\t([^\t]*)\t(.*)/;
- }
- # print "$date > $search ? \n";
- if ($date >$since) {
- $ll --;
- #print "ll=$ll\n";
- last LOOP5 if $ll == -1;
- print "$rest\n";
- }
- }
- if ($ll == -1) {
- print "0**Limit of $limit Reached. Select for more information 0/$short_name/limit $host $myport\r\n";
- $date = &LASTDATE($short_name);
- }
- #print "date=$date\n";
- if ($type==1) {
- &PRINT_EXTRA($short_name,$path1,$host1,$port1,$date,1,"about,whatsnew,top,bookmark");
- } else {
- &PRINT_EXTRA($short_name,$path1,$host1,$port1,$date,1,"about,top,bookmark");
- print ".\r\n";}
- }}
-
- # Binary search -- coding not finished
- # $filelen = (stat(OLD))[7];
- # $lo = 0;
- # $hi = $filelen - 3;
- # NARROW: while (1) {
- # last NARROW if $lo == $hi;
- # $mid = int(($lo + $hi) /2);
- # ($date1,$date2) = &RETURN_TIME($mid);
- # if ($date1 )}
-
- sub RETURN_TIME {
- local($pos) = @_;
- local($c,$date);
- BACK: for (; $pos >= 0; $pos--){
- seek(OLD,$pos,0);
- read(OLD,$c,1);
- if ($c eq "\n") {
- $pos++;
- last BACK;
- }
- }
- seek(OLD,$pos,0);
- $_ = <OLD>;
- ($date1) = /([0-9]{14,14})\t/;
- return($date1);
- }
- }
-
- sub PROCESS_DATE {
- @command = split(" ",@_[0]);
- $key = pop(@command);
- #print "key=$key\n";
-
- $limit = $default_limit;
- if ($key =~ /^limit$/i) {
- $limit = pop(@command);
- $limit = int($limit);
- if ($limit < 0) {$limit = 0;}
- if ($limit == "no") {$limit = -1;}
- $key = pop(@command);
- }
- if ($key =~ /^gophergmt$/i) {
- return(@command[0],$limit);
- } elsif ($key =~ /^ago$/i){
- local($time) = time();
- LOOP1: while(@command) {
- $num = shift(@command);
- $unit = shift(@command);
- #print ">2>",substr($unit,0,1),"<2<\n";
- $factor = @seconds{substr($unit,0,1)};
- if (! $factor) {
- &LOG_MESS("Don't know unit \"$unit\"");
- die("Don't know unit \"$unit\"");
- }
- $time = $time - $num * $factor;
- #print ">3>$time<3<\n";
- }
- if ($time < 0) {$time = 0;}
- local($gopht) = &TIME2GOPHER($time,1);
- #print ">4>$gopht<4<\n";
- return ($gopht,$limit);
- }
- else {
- &LOG_MESS("I don't know time format \"$key\"");
- die("I don't know time format \"$key\"");
- }
- }
-
- sub TIME2GOPHER {
- #print "time=$time\n";
- local($time,$gmt_p) = @_;
- local($cursec,$curmin,$curhour,$curmday,$curmon,$curyear,
- $curwday,$curyday,$curisdst);
- if ($gmt_p) {
- ($cursec,$curmin,$curhour,$curmday,$curmon,$curyear,
- $curwday,$curyday,$curisdst)
- = gmtime($time);
- } else {
- ($cursec,$curmin,$curhour,$curmday,$curmon,$curyear,
- $curwday,$curyday,$curisdst)
- = localtime($time);
- }
- #print ">1>",gmtime(time),"<1<\n";
- $curmon++;
- if ($curyear >= 70) {$curyear = $curyear + 1900;}
- elsif ($curyear < 20) {$curyear = $curyear + 2000;}
- else {die "Sorry, limited to years between 1970 and 2020";}
- return(sprintf("%.4d%.2d%.2d%.2d%.2d%.2d",$curyear,$curmon,
- $curmday,$curhour,$curmin,$cursec));
- }
-
- sub LASTDATE {
- local($short_name) = @_;
- open(OLD,"<$whatsnewdir/$short_name.$oldext") || die "$whatsnewdir/$short_name.$oldext: ",$!;
- local($filelen) = (stat(OLD))[7];
- local($date) = &RETURN_TIME($filelen-3);
- return($date);
- }
-
- sub LOG_MESS {
- local($date) = `date`;
- chop($date);
- $date =~ s/[^ ]* ([0-9]+)$/$1/;# remove timezone info
- open(LOG,">>$logfile")||die("$logfile: $!");
- flock(LOG,2);
- seek(LOG,0,2); # in case someone appended while we waited
- print LOG "$date $$ $theirname : @_[0]\n";
- flock(LOG,8);
- close(LOG);
- }
-
- sub PRINT_EXTRA {
- local($short_name,$path1,$host1,$port1,$date,$star_p,$include) =@_;
- #print "datebpe=$date\n";
-
- if (index($include,"about") >= $[){
- if ($star_p) {print "0**";} else {print "0";}
- print "About \"What's New in $short_name?\" 0/$short_name $host $myport\r\n";}
-
- if (index($include,"whatsnew") >= $[){
- if ($star_p) {print "7**";} else {print "7";}
- print "What's New in $short_name? (e.g.: 1 day 2 hours ago) 7/$short_name $host $myport\r\n";}
-
- if (index($include,"top") >= $[){
- if ($star_p) {print "1**";} else {print "1";}
- print "Top of \"$short_name\" $path1 $host1 $port1\r\n";}
-
- if (index($include,"bookmark") >= $[){
- if ($star_p) {print "1**";} else {print "1";}
- print "Bookmark for future new \"$short_name\" items ($date gophergmt) 1/$short_name/$date gophergmt $host $myport\r\n";}
- }
-
- sub GET_GOPHER_REF {
- local($short_name) = @_;
- #print $short_name,"\n";
- #print "$whatsnewdir/$short_name.$saveext\n";
- if (! open(SAVE,"<$whatsnewdir/$short_name.$saveext")) {
- &LOG_MESS("Can't find file $whatsnewdir/$short_name.$saveext");
- die $!;
- }
- <SAVE>;
- $path1 = <SAVE>; chop($path1);
- $host1 = <SAVE>; chop($host1);
- $port1 = <SAVE>; chop($port1);
- close(SAVE);
- #print "$path1,$host1,$port1\n";
- return($path1,$host1,$port1);
- }
-